﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace FreeDream.Common
{
    /// <summary>
    /// 字符加密解密
    /// </summary>
    public class StringSecurity
    {
        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="input">加密字符</param>
        /// <returns></returns>
        public static string GenerateMD5(string input)
        {
            // Use input string to calculate MD5 hash
            using (MD5 md5 = System.Security.Cryptography.MD5.Create())
            {
                byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);

                // Convert the byte array to hexadecimal string
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    sb.Append(hashBytes[i].ToString("X2"));
                    // To force the hex string to lower-case letters instead of
                    // upper-case, use he following line instead:
                    // sb.Append(hashBytes[i].ToString("x2")); 
                }
                return sb.ToString();
            }
        }

        /// <summary>
        /// MD5流加密
        /// </summary>
        /// <param name="inputStream"></param>
        /// <returns></returns>
        public static string GenerateMD5(Stream inputStream)
        {
            using (MD5 md5 = System.Security.Cryptography.MD5.Create())
            {
                //开始加密
                byte[] newBuffer = md5.ComputeHash(inputStream);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < newBuffer.Length; i++)
                {
                    sb.Append(newBuffer[i].ToString("x2"));
                }
                return sb.ToString();
            }
        }

        //// <summary>
        /// MD5 16位加密 加密后密码为小写
        /// </summary>
        /// <param name="convertString"></param>
        /// <returns></returns>
        public static string GetMd5Str(string convertString)
        {
            //MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            using (MD5 md5 = System.Security.Cryptography.MD5.Create())
            {
                byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(convertString);
                byte[] hashBytes = md5.ComputeHash(inputBytes, 4, 8);
                //var str = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(convertString)), 4, 8);
                string t2 = BitConverter.ToString(hashBytes);
                t2 = t2.Replace("-", "");

                t2 = t2.ToLower();

                return t2;
            }


        }

        /// <summary>
        /// Sha1加密
        /// </summary>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public static string Sha1Encryption(string pwd)
        {
            //创建SHA1加密算法对象
            SHA1 sha1 = SHA1.Create();
            //将原始密码转换为字节数组
            byte[] originalPwd = Encoding.UTF8.GetBytes(pwd);
            //执行加密
            byte[] encryPwd = sha1.ComputeHash(originalPwd);

            //StringBuilder builder = new StringBuilder();
            //foreach (byte hashByte in encryPwd)
            //{
            //    builder.AppendFormat("{0:x2}", hashByte);
            //}
            //var str = builder.ToString();

            //将加密后的字节数组转换为小写字符串
            return string.Join("", encryPwd.Select(b => string.Format("{0:x2}", b)).ToArray()).ToLower();
        }


        /// <summary>
        /// AES解密：从小程序中 getPhoneNumber 返回值中，解析手机号码
        /// </summary>
        /// <param name="encryptedData">包括敏感数据在内的完整用户信息的加密数据，详细见加密数据解密算法</param>
        /// <param name="Session_key"></param>
        /// <param name="iv">加密算法的初始向量</param>
        /// <returns>手机号码</returns>
        public static string AESEncrypt(string encryptedData, string sessionKey, string iv)
        {
            byte[] encryData = Convert.FromBase64String(encryptedData);  // strToToHexByte(text);
            RijndaelManaged rijndaelCipher = new RijndaelManaged();
            rijndaelCipher.Key = Convert.FromBase64String(sessionKey); // Encoding.UTF8.GetBytes(AesKey);
            rijndaelCipher.IV = Convert.FromBase64String(iv);// Encoding.UTF8.GetBytes(AesIV);
            rijndaelCipher.Mode = CipherMode.CBC;
            rijndaelCipher.Padding = PaddingMode.PKCS7;
            ICryptoTransform transform = rijndaelCipher.CreateDecryptor();
            byte[] plainText = transform.TransformFinalBlock(encryData, 0, encryData.Length);
            string result = Encoding.Default.GetString(plainText);
            ////动态解析result 成对象
            //dynamic model = Newtonsoft.Json.Linq.JToken.Parse(result) as dynamic;
            //return model.phoneNumber;
            return result;
        }

        /// <summary>
        /// Aes解密
        /// </summary>
        /// <param name="str">需要解密的字符串</param>
        /// <param name="key">密钥,长度不够时空格补齐,超过时从左截取</param>
        /// <param name="iv">偏移量,长度不够时空格补齐,超过时从左截取</param>
        /// <param name="keyLenth">秘钥长度,16 24 32</param>
        /// <param name="aesMode">解密模式</param>
        /// <param name="aesPadding">填充方式</param>
        /// <returns></returns>
        public static string AesDecode(string str, string key, string iv, int keyLenth = 16, CipherMode aesMode = CipherMode.CBC, PaddingMode aesPadding = PaddingMode.PKCS7)
        {
            str = str.Replace(" ", "+");
            key = key.Replace(" ", "+");
            iv = iv.Replace(" ", "+");
            if (!new List<int> { 16, 24, 32 }.Contains(keyLenth))
            {
                return null;//密钥的长度，16位密钥 = 128位，24位密钥 = 192位，32位密钥 = 256位。
            }
            var oldBytes = Convert.FromBase64String(str);
            var bKey = new Byte[keyLenth];
            Array.Copy(Convert.FromBase64String(key.PadRight(keyLenth)), bKey, keyLenth);
            var bIv = new Byte[16];
            Array.Copy(Convert.FromBase64String(iv.PadRight(16)), bIv, 16);

            var rijalg = new RijndaelManaged
            {
                Mode = aesMode,
                Padding = aesPadding,
                Key = bKey,
                IV = bIv,
            };
            var decryptor = rijalg.CreateDecryptor(rijalg.Key, rijalg.IV);
            var rtByte = decryptor.TransformFinalBlock(oldBytes, 0, oldBytes.Length);
            return Encoding.UTF8.GetString(rtByte);
        }

        #region AES加密解密

        /// <summary>
        ///  加密AES 参数：string
        /// </summary>
        /// <param name="strCon">加密内容</param>
        /// <param name="key">密钥</param>
        /// <returns>string：密文</returns>
        public static string Encrypt(string strCon, string key)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(strCon))
                {
                    return null;
                }

                byte[] byCon = Encoding.UTF8.GetBytes(strCon);
                var rm = new RijndaelManaged
                {
                    IV = Encoding.UTF8.GetBytes(key),
                    Key = Encoding.UTF8.GetBytes(key),
                    Mode = CipherMode.CBC,
                    Padding = PaddingMode.PKCS7
                };
                ICryptoTransform cTransform = rm.CreateEncryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(byCon, 0, byCon.Length);
                return Convert.ToBase64String(resultArray, 0, resultArray.Length);
            }
            catch
            {
                return "";
            }
        }

        /// <summary>
        /// 解密AES
        /// </summary>
        /// <param name="strCon"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string Decrypt(string strCon, string key)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(strCon))
                {
                    return null;
                }

                byte[] byCon = Convert.FromBase64String(strCon);
                var rm = new RijndaelManaged
                {
                    IV = Encoding.UTF8.GetBytes(key),
                    Key = Encoding.UTF8.GetBytes(key),
                    Mode = CipherMode.CBC,
                    Padding = PaddingMode.PKCS7
                };
                ICryptoTransform cTransform = rm.CreateDecryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(byCon, 0, byCon.Length);
                return Encoding.UTF8.GetString(resultArray);
            }
            catch
            {
                return "";
            }
        }

        /// <summary>
        ///  AES 加密
        /// </summary>
        /// <param name="str">明文</param>
        /// <param name="aesKey">密钥</param>
        /// <returns></returns>
        public static string AesEncrypt(string str, string aesKey)
        {
            string data = string.Empty;
            if (!string.IsNullOrEmpty(str) && !string.IsNullOrEmpty(aesKey))
            {
                byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);
                Aes aes = Aes.Create();
                aes.Key = Encoding.UTF8.GetBytes(aesKey);
                byte[] resultArray = aes.EncryptEcb(toEncryptArray, PaddingMode.PKCS7);
                data = Convert.ToBase64String(resultArray, 0, resultArray.Length);
            }
            return data;
        }

        /// <summary>
        ///  AES 解密
        /// </summary>
        /// <param name="str">密文</param>
        /// <param name="aesKey">密钥</param>
        /// <returns></returns>
        public static string AesDecrypt(string str, string aesKey)
        {
            string data = string.Empty;
            if (!string.IsNullOrEmpty(str) && !string.IsNullOrEmpty(aesKey))
            {
                byte[] toEncryptArray = Convert.FromBase64String(str);
                Aes aes = Aes.Create();
                aes.Key = Encoding.UTF8.GetBytes(aesKey);
                byte[] resultArray = aes.DecryptEcb(toEncryptArray, PaddingMode.PKCS7);
                data = Encoding.UTF8.GetString(resultArray);
            }
            return data;
        }

        /// <summary>
        /// AES加密解密
        /// </summary>
        /// <param name="str"></param>
        /// <param name="aesKey"></param>
        /// <returns></returns>
        public static string AesEnDecrype(string str, string aesKey)
        {
            //byte[] plaintext = new byte[] { 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 };
            byte[] plaintext = Encoding.UTF8.GetBytes(str);
            using (var aes = new RijndaelManaged())
            {
                aes.Key = Encoding.UTF8.GetBytes(aesKey);//new byte[] { 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 };
                aes.IV = new byte[16];
                //aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.None;
                var cryptoTransform = aes.CreateEncryptor();
                byte[] resultBuff = cryptoTransform.TransformFinalBlock(plaintext, 0, plaintext.Length);
                foreach (byte i in resultBuff)
                    Console.Write("{0:X2}", i);
                //将加密后的字节数组转换为小写字符串
                Console.WriteLine(string.Join("", resultBuff.Select(b => string.Format("{0:X2}", b)).ToArray()).ToLower());
                var res1 = Convert.ToBase64String(resultBuff, 0, resultBuff.Length);    //  加密结果

                //解密
                cryptoTransform = aes.CreateDecryptor();
                resultBuff = cryptoTransform.TransformFinalBlock(resultBuff, 0, resultBuff.Length);
                foreach (byte i in resultBuff)
                    Console.Write("{0:X2}", i);
                Console.WriteLine(string.Join("", resultBuff.Select(b => string.Format("{0:X2}", b)).ToArray()).ToLower());
                var res2 = Encoding.UTF8.GetString(resultBuff);

                return res1 + "," + res2;
            }
        }

        #endregion

        #region RSA

        /// <summary>
        /// 获取加密所使用的key，RSA算法是一种非对称密码算法，所谓非对称，就是指该算法需要一对密钥，使用其中一个加密，则需要用另一个才能解密。
        /// </summary>
        public static string GetKey()
        {
            string PublicKey = string.Empty;
            string PrivateKey = string.Empty;
            RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider();
            PublicKey = rSACryptoServiceProvider.ToXmlString(false);
            // 获取公匙，用于加密
            PrivateKey = rSACryptoServiceProvider.ToXmlString(true);
            // 获取公匙和私匙，用于解密

            //Console.WriteLine("PublicKey is {0}", PublicKey);        // 输出公匙
            //Console.WriteLine("PrivateKey is {0}", PrivateKey);     // 输出密匙

            //密匙中含有公匙，公匙是根据密匙进行计算得来的。
            using (StreamWriter streamWriter = new StreamWriter("PublicKey.xml"))
            {
                streamWriter.Write(rSACryptoServiceProvider.ToXmlString(false));// 将公匙保存到运行目录下的PublicKey
            }
            using (StreamWriter streamWriter = new StreamWriter("PrivateKey.xml"))
            {
                streamWriter.Write(rSACryptoServiceProvider.ToXmlString(true)); // 将公匙&私匙保存到运行目录下的PrivateKey
            }

            return $"{DateTime.Now}:{PublicKey},{PrivateKey}";
        }

        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string RSAEncryption(string str)
        {
            RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider();
            using (StreamReader streamReader = new StreamReader("PublicKey.xml")) // 读取运行目录下的PublicKey.xml
            {
                rSACryptoServiceProvider.FromXmlString(streamReader.ReadToEnd()); // 将公匙载入进RSA实例中
            }

            byte[] buffer = Encoding.UTF8.GetBytes(str); // 将明文转换为byte[]

            // 加密后的数据就是一个byte[] 数组,可以以 文件的形式保存 或 别的形式(网上很多教程,使用Base64进行编码化保存)
            byte[] EncryptBuffer = rSACryptoServiceProvider.Encrypt(buffer, false); // 进行加密
            return Convert.ToBase64String(EncryptBuffer); // 如果使用base64进行明文化，在解密时 需要再次将base64 转换为byte[]
        }

        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="strEncryptBase64"></param>
        /// <returns></returns>
        public static string RSADecrypt(string strEncryptBase64)
        {
            RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider();
            using (StreamReader streamReader = new StreamReader("PrivateKey.xml")) // 读取运行目录下的PrivateKey.xml
            {
                rSACryptoServiceProvider.FromXmlString(streamReader.ReadToEnd()); // 将私匙载入进RSA实例中
            }

            byte[] buffer = Convert.FromBase64String(strEncryptBase64);
            // 解密后得到一个byte[] 数组
            byte[] DecryptBuffer = rSACryptoServiceProvider.Decrypt(buffer, false); // 进行解密
            string str = Encoding.UTF8.GetString(DecryptBuffer); // 将byte[]转换为明文

            return str;
        }

        #region 解决长字符加密的异常，我们采取分段加密的方法进行字符串加密

        /// <summary>
        /// 分段加密
        /// </summary>
        /// <param name="xmlPublicKey"></param>
        /// <param name="enptStr"></param>
        /// <returns></returns>
        public static string SubRSAEncrypt(string xmlPublicKey, string enptStr)
        {
            RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
            provider.FromXmlString(xmlPublicKey);
            byte[] bytes = Encoding.UTF8.GetBytes(enptStr);
            int MaxBlockSize = provider.KeySize / 8 - 11;  //加密块最大长度限制​
            if (bytes.Length <= MaxBlockSize)
            {
                var str = Convert.ToBase64String(provider.Encrypt(bytes, false));
                return str;
            }
            using (MemoryStream PlaiStream = new MemoryStream(bytes))
            {
                using (MemoryStream CrypStream = new MemoryStream())
                {

                    byte[] Buffer = new byte[MaxBlockSize];
                    int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    while (BlockSize > 0)
                    {
                        byte[] ToEncrypt = new byte[BlockSize];
                        Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);
                        byte[] Cryptograph = provider.Encrypt(ToEncrypt, false);
                        CrypStream.Write(Cryptograph, 0, Cryptograph.Length);
                        BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    }
                    return Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
                }
            }
        }

        /// <summary>
        /// 分段解密
        /// </summary>
        /// <param name="xmlPublicKey"></param>
        /// <param name="enptStr"></param>
        /// <returns></returns>
        public static string SubRSADecrypt(string xmlPublicKey, string enptStr)
        {
            RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
            provider.FromXmlString(xmlPublicKey);
            byte[] bytes = Convert.FromBase64String(enptStr);
            int MaxBlockSize = provider.KeySize / 8;  //解密块最大长度限制​
            if (bytes.Length <= MaxBlockSize)
            {
                return Encoding.UTF8.GetString(provider.Decrypt(bytes, false));
            }
            using (MemoryStream CrypStream = new MemoryStream(bytes))
            using (MemoryStream PlaiStream = new MemoryStream())
            {
                byte[] Buffer = new byte[MaxBlockSize];
                int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
                while (BlockSize > 0)
                {
                    byte[] ToDecrypt = new byte[BlockSize];
                    Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);
                    byte[] Plaintext = provider.Decrypt(ToDecrypt, false);
                    PlaiStream.Write(Plaintext, 0, Plaintext.Length);
                    BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
                }
                return Encoding.UTF8.GetString(PlaiStream.ToArray());
            }
        }

        #endregion

        #endregion
    }
}
